<template>
  <div class="dependency-tab">
    <div class="upper-selector">
      <!-- 选择框设置 -->
      <el-select v-model="value" placeholder="请选择" size="small">
        <el-option
          v-for="item in selecterValue"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        />
      </el-select>
      <el-button
        v-if="value==='option1' || value==='option4'"
        class="layout-button"
        :disabled="selectedAdd.length === 0 && selectedDelete.length === 0 && value !== 'option4'"
        :loading="isUpdating"
        size="small"
        style="background: #ffa54f; color: white; float: right;"
        :class="{'is-transparent': selectedAdd.length === 0 && selectedDelete.length === 0 && value !== 'option4'}"
        @click="updateDependency"
      >
        更新依赖
      </el-button>
      <el-button
        v-if="value==='option2'"
        class="layout-button"
        :disabled="selectedAdd.length === 0 && selectedDelete.length === 0 && value !== 'option4'"
        :loading="isUpdating"
        size="small"
        style="background: #ffa54f; color: white; float: right;"
        :class="{'is-transparent': selectedAdd.length === 0 && selectedDelete.length === 0 && value !== 'option4'}"
        @click="updateDependency"
      >
        应用更改
      </el-button>
      <el-button
        class="layout-button"
        :disabled="isDownloading === false"
        size="small"
        style="background: #ffa54f; color: white; float: right;"
        :class="{'is-transparent': isDownloading === false}"
        @click="realTimeUpdateLog = true"
      >
        安装进度
      </el-button>
      <el-button
        class="layout-button"
        :disabled="isDownloading === true || logInformation === ''"
        size="small"
        style="background: #ffa54f; color: white; float: right;"
        :class="{'is-transparent': isDownloading === true || logInformation === ''}"
        @click="showLogInformation = true"
      >
        安装日志
      </el-button>
      <el-button
        class="layout-button"
        size="small"
        style="background: #ffa54f; color: white; float: right;"
        @click="isSelectPublicLayer = true"
      >
        基础模板
      </el-button>
    </div>
    <div class="bottom-table">
      <div v-if="!isLoadingInstalledPackages">
        <el-dialog title="安装进度" :visible.sync="realTimeUpdateLog" width="50%" center>
          <pre
            id="realLogText"
            v-loading="realTimeLog === ''"
            class="function-view-log h-max"
            style="padding-left: 10px;"
          >{{ realTimeLog }}</pre>
        </el-dialog>
        <el-dialog title="更新日志" :visible.sync="showLogInformation" width="50%" center>
          <pre
            id="logInformationText"
            class="function-view-log h-max"
            style="padding-left: 10px;"
          >{{ logInformation }}</pre>
        </el-dialog>
        <el-dialog title="基础模板" :visible.sync="isSelectPublicLayer" width="50%" center>
          <el-table :data="publicLayer" height="500px" stripe>
            <el-table-column label="基础模板" prop="name" align="center" width="250px" />
            <el-table-column label="详情" type="expand" align="left">
              <template #default="props">
                <div class="display-window">
                  此基础库将包含以下依赖:
                  {{ props.row.contains }}
                </div>
              </template>
            </el-table-column>
            <el-table-column label="操作" align="center">
              <template slot-scope="scope">
                <el-button
                  size="mini"
                  :disabled="selectPublicLayer !== '' && selectPublicLayer !== scope.row.name"
                  @click="selectPublicLayer2Use(scope.row.name, scope.row.name !== selectPublicLayer)"
                >
                  <div v-if="scope.row.name !== selectPublicLayer">
                    选择并应用
                  </div>
                  <div v-else>
                    取消应用
                  </div>
                </el-button>
              </template>
            </el-table-column>
          </el-table>
        </el-dialog>
        <template v-if="value==='option1'">
          <el-input v-model="searchInput" placeholder="输入关键字搜索" class="searchBox" />
        </template>

        <div v-if="value==='option2'" key="op2">
          <!-- 已安装的 -->
          <div class="option2-tables">
            <el-table :data="installed" class="left-table" height="65vh" stripe>
              <el-table-column label="已安装包" prop="name" width="200px" show-overflow-tooltip align="center" />
              <el-table-column label="版本" prop="version" align="center" />
              <el-table-column label="操作" width="100px" align="center">
                <template slot-scope="scope">
                  <el-button
                    v-if="isPackageCmpTrue(scope.row.name, selectedDelete) === false"
                    :disabled="isPackageCmpTrue(scope.row.name, userInstalled) === false"
                    size="mini"
                    @click="deletePackage(scope.row.name, scope.row.version)"
                  >
                    删除
                  </el-button>
                  <el-button v-else size="mini" type="danger" @click="cancelDelete(scope.row.name)">
                    取消
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
            <div class="right-tables">
              <el-table :data="selectedAdd" height="65vh" class="right-upper-table" stripe>
                <el-table-column label="即将安装" prop="name" width="200px" align="center" />
                <el-table-column label="版本" prop="version" align="center" />
                <el-table-column label="操作" width="100px" align="center">
                  <template slot-scope="scope">
                    <el-button
                      v-if="isPackageCmpTrue(scope.row.name, selectedAdd)"
                      size="mini"
                      @click="cancelAdd(scope.row.name)"
                    >
                      取消
                    </el-button>
                  </template>
                </el-table-column>
              </el-table>
              <el-table :data="selectedDelete" height="65vh" class="right-buttom-table" stripe>
                <el-table-column label="即将删除" prop="name" width="200px" align="center" />
                <el-table-column label="版本" prop="version" align="center" />
                <el-table-column label="操作" width="100px" align="center">
                  <template slot-scope="scope">
                    <el-button
                      v-if="isPackageCmpTrue(scope.row.name, selectedDelete)"
                      size="mini"
                      @click="cancelDelete(scope.row.name)"
                    >
                      取消
                    </el-button>
                  </template>
                </el-table-column>
              </el-table>
            </div>
          </div>
        </div>
        <div v-else-if="value==='option4'" key="op4">
          <div class="editor-tab">
            <editor
              ref="editors"
              v-model="requirementFile.editContent"
              class="editor"
              :options="editorOptions"
              theme="chrome"
              width="100%"
              lang="python"
              @init="editorInit"
              @input="(event) => { requirementFile.editContent = event; }"
            />
          </div>
        </div>
        <div v-else key="op1">
          <!-- 所有的依赖安装界面 -->
          <div>
            <el-table
              height="55vh"
              width="95vw"
              :data="searchedPackages"
              :row-style="{ height: 40 + 'px' }"
              :cell-style="{ padding: 0 + 'px' }"
              stripe
            >
              <el-table-column label="状态" width="200px" align="center">
                <template slot-scope="scope">
                  <el-tag v-if="!isPackageCmpTrue(scope.row.name, installed)" type="info" effect="plain">
                    未安装
                  </el-tag>
                  <el-tag v-if="isPackageCmpTrue(scope.row.name, installed)" type="success" effect="plain">
                    已安装
                  </el-tag>
                  <el-tag v-if="isPackageCmpTrue(scope.row.name, selectedAdd)" effect="plain">
                    选定安装
                  </el-tag>
                  <el-tag v-if="isPackageCmpTrue(scope.row.name, selectedDelete)" type="danger" effect="plain">
                    选定删除
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="包名" prop="name" width="200px" show-overflow-tooltip align="left" />
              <el-table-column label="描述" prop="description" show-overflow-tooltip align="left" />
              <el-table-column label="版本" prop="version" width="200px" align="left">
                <template slot-scope="scope">
                  <div>
                    <span style="margin-right: 10px">{{ scope.row.version }}</span>
                    <el-dropdown v-if="scope.row.all_version.length>1 && !isPackageCmpTrue(scope.row.name, installed)" trigger="click">
                      <span class="el-dropdown-link">
                        <i class="el-icon-more" />
                      </span>
                      <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item
                          v-for="item in scope.row.all_version"
                          :key="item"
                          @click.native="updatePackageVersion(item, scope.row)"
                        >{{ item }}</el-dropdown-item>
                      </el-dropdown-menu>
                    </el-dropdown>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="100px" align="center">
                <template slot-scope="scope">
                  <div v-if="isPackageCmpTrue(scope.row.name, installed) === false">
                    <el-button
                      v-if="isPackageCmpTrue(scope.row.name, selectedAdd) === false"
                      size="mini"
                      @click="addPackage(scope.row.name, scope.row.version)"
                    >
                      添加
                    </el-button>
                    <el-button v-else size="mini" type="danger" @click="cancelAdd(scope.row.name)">
                      取消
                    </el-button>
                  </div>
                  <div v-else>
                    <el-button
                      v-if="isPackageCmpTrue(scope.row.name, selectedDelete) === false"
                      :disabled="isPackageCmpTrue(scope.row.name, userInstalled) === false"
                      size="mini"
                      @click="deletePackage(scope.row.name, scope.row.version)"
                    >
                      删除
                    </el-button>
                    <el-button v-else size="mini" type="danger" @click="cancelDelete(scope.row.name)">
                      取消
                    </el-button>
                  </div>
                </template>
              </el-table-column>
            </el-table>
            <div class="pagination">
              <div class="inpagination">
                <el-pagination
                  background
                  layout="prev, pager, next"
                  :pager-count="5"
                  :current-page="pageComponent.page"
                  :page-size="pageComponent.pageSize"
                  :total="pageComponent.total"
                  style="text-align: center;"
                  @current-change="goPage"
                />
              </div>
            </div>
          </div>
        </div>
      </div>
      <el-skeleton v-else class="el-skeleton" :rows="15" animated />
    </div>
  </div>
</template>

<script>
import { downloadOss, searchRequirements } from '@/api/userProject';
import { createLayerForLibrary, getLayerDependencyForLibrary, getPublicLayerForLibrary } from '@/api/userProjectLibrary';
import { debounce } from 'lodash-es';
import editor from 'vue2-ace-editor';
import { mapActions, mapMutations, mapState } from 'vuex';
import { getObjectUrl } from '@/utils/aliOss';
import user from '@/store/modules/user';
// import { PassThrough } from 'stream';

export default {
  name: 'DependencyTab',
  components: {
    // InstalledPackage,
    editor
  },
  props: {
    tabHeight: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      selecterValue: [{
        value: 'option1',
        label: '所有依赖'
      }, {
        value: 'option2',
        label: '函数包管理界面'
      }, {
        value: 'option4',
        label: '使用requirements文件'
      }],
      value: 'option1',
      editorOptions: {
        enableBasicAutocompletion: true,
        enableSnippets: true,
        enableLiveAutocompletion: true,
        tabSize: 2,
        fontSize: 15,
        scrollbar: true,
        showPrintMargin: false // 去除编辑器里的竖线
      },
      isLoadingInstalledPackages: false,
      isUpdating: false,
      searchedPackages: undefined,
      pageComponent: {
        page: 0,
        pageSize: 10,
        total: 0
      },
      installed: [],
      userInstalled: [],
      layerInstalled: [],
      searchInput: '',
      selectedAdd: [],
      selectedDelete: [],
      searching: false,
      isDownloading: false,
      realTimeUpdateLog: false,
      realTimeLog: '',
      versionsTab: [],
      tooltipContent: '使用requirements.txt文件，直接更新需要的包版本。请注意，不要与选择器中已经安装的包发生冲突。',
      logInformation: '',
      showLogInformation: false,
      publicLayer: [],
      isSelectPublicLayer: false,
      selectPublicLayer: ''
    };
  },
  computed: {
    ...mapState('functions', ['templateFiles', 'isUploadTemplateFile', 'serviceName', 'functionName']),
    requirementFile() {
      return this.templateFiles.requirement;
    }
  },
  watch: {
    searchInput: function(newSearch) {
      this.debouncedGetRequirements(newSearch, this.pageComponent.pageSize, (this.pageComponent.page - 1) * this.pageComponent.pageSize);
    },
    realTimeLog: function() {
      this.$nextTick(() => {
        const textarea = document.getElementById('realLogText');
        textarea.scrollTop = textarea.scrollHeight;
      });
    },
    realTimeUpdateLog: function() {
      var interval;
      if (this.realTimeUpdateLog === true) {
        this.realTimeLog = '';
        const name = `projectLayer/` + user.state.uid.toString() + `/` + this.serviceName.substring(66) + '_' + this.functionName + `/progress.txt`;
        const url = getObjectUrl(name);
        interval = setInterval(() => {
          url
            .then((url) => {
              downloadOss(url)
                .then((resp) => {
                  resp.text().then((text) => {
                    this.realTimeLog = text;
                    if (/please wait$/g.test(text)) {
                      this.isDownloading = false;
                      clearInterval(interval);
                    }
                  });
                });
            });
        }, 2000);
      } else if (this.realTimeUpdateLog === false) {
        // this.isDownloading = false;
        if (interval) clearInterval(interval);
      }
    }
  },
  created() {
    this.debouncedGetRequirements = debounce(this.getRequirements, 500);
    this.getRequirements('', 10, 0);
    this.isLoadingInstalledPackages = true;
    this.requirementFile.editContent = '# -*- coding: UTF-8 -*-\n' +
      '# 可在本文件中写入依赖信息\n' +
      '# 比如: cycler==0.10.0\n' +
      '# 填好信息后点击右上角的更新依赖即可为函数拉取依赖\n';
    getPublicLayerForLibrary().then((res) => {
      const data = [];
      let i;
      for (i = 0; i < res.Data.Names.length; i++) {
        data.push({ name: res.Data.Names[i], arn: res.Data.Arn[i], contains: res.Data.Contains[i] });
      }
      this.publicLayer = data;
    });
    getLayerDependencyForLibrary(this.$store.state.functions.serviceName, this.$store.state.functions.functionName).then((res) => {
      this.installed = JSON.parse(res.PackagesJson);
      this.userInstalled = JSON.parse(res.UserLayerJson);
      const publicLayerName = JSON.parse(res.BaseLayerJson);
      if (publicLayerName.length !== 0) {
        const name = publicLayerName[0].name;
        this.selectPublicLayer = name;
        const publicLayer = this.publicLayer.filter((x) => name === x.name);
        this.$store.state.functions.usePublicLayers = publicLayer[0].arn;
      } else {
        this.$store.state.functions.usePublicLayers = '';
      }
    }).catch(() => {
      this.installed = [];
    }).finally(() => {
      this.isLoadingInstalledPackages = false;
    });
  },

  mounted() {
    document.addEventListener('keydown', this.saveEvent);
  },
  destroyed() {
    document.removeEventListener('keydown', this.saveEvent);
  },

  methods: {
    ...mapMutations('functions', ['setShowInstalledDialog']),
    ...mapActions('functions', ['buildTemplateFile']),
    getRequirements(name, limit, offset) {
      this.searching = true;
      searchRequirements({ name, limit, offset }).then((resp) => {
        const { total, names, lastest_version, all_versions, description } = resp;
        this.pageComponent.total = total;
        if (names?.length) {
          const data = [];
          let i;
          for (i = 0; i < names.length; i++) {
            data.push({ name: names[i], version: lastest_version[i], all_version: all_versions[i].replaceAll(',', '').split(' '), description: description[i] });
          }
          this.searchedPackages = data;
        }
      }).finally(() => {
        this.searching = false;
      });
    },
    saveEvent(e) { // TODO 重构 和mainTab下代码重复
      // js监听键盘ctrl+s快捷键保存
      if (
        e.keyCode === 83 &&
          (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)
      ) {
        e.preventDefault();
        if (this.isUploadTemplateFile[this.requirementFile.id]) {
          this.$message.error('文件正在部署中...');
          return;
        }
        if (this.requirementFile.editText !== this.requirementFile.editContent) {
          this.buildTemplateFile(this.requirementFile);
        }
      }
    },
    // 使用下拉式窗口更新版本
    updatePackageVersion(version, obj) {
      obj.version = version;
      if (this.isPackageCmpTrue(obj.name, this.selectedAdd)) {
        this.cancelAdd(obj.name);
        this.addPackage(obj.name, obj.version);
      }
    },
    // 使用公共层
    selectPublicLayer2Use(name, ifSelect) {
      if (ifSelect) {
        this.$confirm('将要使用新的AI公共依赖包？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.selectPublicLayer = name;
          const publicLayer = this.publicLayer.filter((x) => name === x.name);
          this.$store.state.functions.usePublicLayers = publicLayer[0].arn;
          const layerList = publicLayer[0].contains.split(',');
          for (var i = 0; i < layerList.length; i++) {
            const splitResult = layerList[i].split('==');
            this.layerInstalled.push({ name: splitResult[0], version: splitResult[1] });
          }
          this.installed = this.userinstalled.concat(this.layerInstalled);
        }).then(() => {
          this.$message({
            message: `请在更改完所有依赖后，重新部署函数从而使依赖生效。`,
            type: 'success',
            offset: 40
          });
        });
      } else {
        this.selectPublicLayer = '';
        this.$store.state.functions.usePublicLayers = '';
        this.layerInstalled = [];
        this.installed = this.userInstalled;
        this.$message({
          message: `请在更改完所有依赖后，重新部署函数从而使依赖生效。`,
          type: 'success',
          offset: 40
        });
      }
    },
    // 更新依赖
    updateDependency() {
      if (this.value === 'option1') {
        this.$confirm('是否进入函数包管理界面确定是否应用更改？', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'info'
        }).then(() => {
          this.value = 'option2';
        });
      } else {
        this.isUpdating = true;
        // 合并已安装 将要删除 将要添加
        const installed = [];
        this.userInstalled.forEach((item) => installed.push(item));
        // 去除删除的包内容
        const arr = installed.filter((x) => !this.selectedDelete.some((item) => x.name === item.name));
        // 这个地方是根据对应的option来对不同界面下的requirements进行更新的。
        let requirementsString = '';
        if (this.value === 'option4') {
          requirementsString = this.requirementFile.editContent.trim() + '\n';
          this.userInstalled.forEach((item) => {
            requirementsString += item.name + '==' + item.version + '\n';
          });
        } else {
          arr.forEach((item) => {
            requirementsString += item.name + '==' + item.version + '\n';
          });
          this.selectedAdd.forEach((item) => {
            requirementsString += item.name + '==' + item.version + '\n';
          });
        }
        requirementsString = requirementsString.trim() + '\n';
        this.isDownloading = true;
        this.logInformation = '';
        createLayerForLibrary(this.serviceName, this.functionName, requirementsString).then((res) => {
          this.selectedAdd = [];
          this.selectedDelete = [];
          this.isUpdating = false;
          this.isLoadingInstalledPackages = true;
          this.isDownloading = false;
          this.realTimeUpdateLog = false;
          this.logInformation = res.Rmsg;
          getLayerDependencyForLibrary(this.serviceName, this.functionName).then((res) => {
            this.installed = JSON.parse(res.PackagesJson);
            this.userInstalled = JSON.parse(res.UserLayerJson);
          }).catch(() => {
            this.installed = [];
          }).finally(() => {
            this.isLoadingInstalledPackages = false;
            this.requirementFile.editContent = '# -*- coding: UTF-8 -*-\n' +
                '# 可在本文件中写入依赖信息\n' +
                '# 比如: cycler==0.10.0\n' +
                '# 填好信息后点击右上角的更新依赖即可为函数拉取依赖\n';
            this.$message({
              message: `请在更改完所有依赖后，重新部署函数从而使依赖生效。`,
              type: 'success',
              offset: 40
            });
          });
        });
      }
    },

    // 跳转
    goPage(val) {
      this.pageComponent.page = val;
      this.getRequirements(this.searchInput, this.pageComponent.pageSize, (this.pageComponent.page - 1) * this.pageComponent.pageSize);
    },
    isPackageCmpTrue(name, cmpList) {
      for (const p of cmpList) {
        if (p !== undefined && p.name === name) {
          return true;
        }
      }
      return false;
    },
    // 添加package
    addPackage(name, version) {
      // 其实应该是添加name与version了 this.selectAdd.push(name, version);
      this.selectedAdd.push({ name, version });
    },
    // 取消添加package
    cancelAdd(name) {
      this.selectedAdd = this.selectedAdd.filter((x) => x.name !== name);
    },
    deletePackage(name, version) {
      this.selectedDelete.push({ name, version });
    },
    cancelDelete(name) {
      this.selectedDelete = this.selectedDelete.filter((x) => x.name !== name);
    },

    editorInit() {
      require('brace/ext/language_tools');
      require('brace/mode/python');
      require('brace/theme/chrome');
      require('brace/mode/less');
      require('brace/snippets/python');
    }
  }
};
</script>

<style lang="scss" scoped>

.dependency-tab {
  display: flex;
  flex-direction: column;
  .upper-selector {
    height: auto;
  }
  .bottom-table {
    height: auto;

  }
}

.layout-button {
  background: #ffa54f;
  color: white;
  margin-right: 10px;
  margin-bottom: 10px;
  margin-left: 10px;
}
.option2-tables {
  border-top: 1px solid #d5d5d5;
  border-bottom: 1px solid #d5d5d5;
  display: flex;
  flex-direction: row;
  .left-table {
    height: 65vh;
    width: 55vw;
    border-right: 1px solid #d5d5d5;
  }
  .right-tables {
    height: 65vh;
    width: 45vw;
    display: flex;
    flex-direction: column;
    .right-upper-table {
      height: 33vh;
      width: 100%;
    }
    .right-buttom-table {
      height: 32vh;
      width: 100%;
    }
  }
}

.editor-tab {
  display: flex;
  flex-direction: column;
  height: 65vh;
  width: 100%;
  flex: 1;
  margin-bottom: 15px;
  .editor {
    height: 65vh;
    border-bottom: 1px solid #d5d5d5;
  }
}
.is-transparent {
  opacity: 0.5;
}

.el-tag {
  margin-left: 10px;
}
.searchBox {
  width: 25%;
  height: 5vh;
}
.function-view-log {
  height: 300px;
  overflow: auto;
  background-color: #263238;
  white-space: pre-wrap;
  line-height: 18px;
  font-family: Consolas, Menlo, Courier, monospace, -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif;
  font-size: 14px;
  font-variant-ligatures: contextual;
  -webkit-font-smoothing: antialiased;
  color: #eff;
}
  .display-window {
      margin-right: 10px;
      margin-bottom: 10px;
      margin-left: 10px;
      font-weight: bolder;
          background:rgb(238, 238, 238);
  }
</style>
